# @author: Samuel Taylor <samtaylor.uk@gmail.com>
# @package: winecellar
# @license: GNU GPLv3
#
# Copyright (C) 2010  Samuel Taylor <samtaylor.uk@gmail.com>
#----------------------------------------------------------------------
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#-----------------------------------------------------------------------

__author__ = 'Samuel Taylor <samtaylor.uk@gmail.com>'

import sys
from optparse import OptionParser
import time
import threading
import gtk
import pygtk
import gobject
import WineCellar.helpers
from WineCellar.Preferences import Preferences
from WineCellar.RepositoryManager import RepositoryManager
from WineCellar.BottleManager import BottleManager
from WineCellar.Bottle import Bottle
from WineCellar.ApplicationPack import ApplicationPack
import WineCellar.Logger
import WineCellar.Config

#gtk.gdk.threads_init()

class FirstRunWindow:

    def __init__(self):
        self.builder = WineCellar.helpers.get_builder('gtk_windowFirstRun')
        self.window = self.builder.get_object("windowFirstRun")
        if self.window:
            self.window.connect("destroy", gtk.main_quit)
        
        dic = { 
            "on_buttonQuit_clicked" : self.quit,
            "on_buttonOk_clicked" : self.on_buttonOk_clicked,
            "on_windowFirstRun_destroy" : self.quit,
        }
        
        self.builder.connect_signals(dic)
        self.window.show()

    def on_buttonOk_clicked(self, widget):
        name = self.builder.get_object("entryName").get_text()
        company = self.builder.get_object("entryCompany").get_text()
        if name == "" or company == "":
            self.builder.get_object("hboxError").show()
        else:
            Preferences().setKey("owner", name)
            Preferences().setKey("company", company)
            MainWindow()
            self.window.destroy()
        return 0
    
    def quit(self, widget):
        sys.exit(0)


class MainWindow:

    def __init__(self):
        self.builder = WineCellar.helpers.get_builder('gtk_windowMain')
        self.window = self.builder.get_object("windowMain")
        if self.window:
            self.window.connect("destroy", gtk.main_quit)
        
        dic = { 
            "on_windowMain_destroy" : self.quit,
            "on_toolbuttonReload_clicked" : self.on_toolbuttonReload_clicked,
            "on_treeviewPackages_cursor_changed" : self.on_treeviewPackages_cursor_changed,
            "on_treeviewCats_cursor_changed" : self.on_treeviewCats_cursor_changed,
            "on_buttonInstall_clicked" : self.on_buttonInstall_clicked
        }
        
        self.builder.connect_signals(dic)
        
        #Here are some variables that can be reused later
        self.cName = 2
        self.cDescirption = 3
        		
        self.sName = "Name"
        self.sDescirption = "Descirption"	
				
        #Get the treeView from the widget Tree
        self.packagesView = self.builder.get_object("treeviewPackages")
        self.buttonInstall = self.builder.get_object("buttonInstall")

        render = gtk.CellRendererText()
        	
        column = gtk.TreeViewColumn("shortname", render
	        , text=0)
        column.set_sort_column_id(0)
        column.set_visible(False)
        self.packagesView.append_column(column)

        column = gtk.TreeViewColumn()
        column.set_title(self.sName)
        self.packagesView.append_column(column)

        rendererer = gtk.CellRendererPixbuf()
        column.pack_start(rendererer, expand=False)
        column.add_attribute(rendererer, 'pixbuf', 1)

        renderer = gtk.CellRendererText()
        column.pack_start(renderer, expand=True)
        column.add_attribute(renderer, 'text', self.cName)
        
        self.AddWineListColumn(self.sDescirption, self.cDescirption)
        
        #Create the listStore Model to use with the wineView
        self.packagesList = gtk.ListStore(str, gtk.gdk.Pixbuf, str, str)
        #Attach the model to the treeView
        self.packagesView.set_model(self.packagesList)
	    
        self.updatePackagesList()
	    
        self.catView = self.builder.get_object("treeviewCats")
        self.catList = gtk.ListStore(str)
        self.catView.set_model(self.catList)
        
        column = gtk.TreeViewColumn("Category", gtk.CellRendererText()
	        , text=0)
        column.set_resizable(False)		
        column.set_sort_column_id(0)
        self.catView.append_column(column)
        
        self.catList.append(["All"])

        for category in RepositoryManager().getCategories():
            self.catList.append([category])
		
        self.window.show()
    
    def updatePackagesList(self, cat = None):
        self.packagesList.clear()
        
        thr = threading.Thread(target=self.updatePackages, args=(cat,))
        thr.start()
        self.builder.get_object("statusText").set_text("Loading")
        while thr.is_alive():
            time.sleep(0.1)
            self.builder.get_object("statusProgressbar").pulse()
            self.refreshGui()
        self.builder.get_object("statusText").set_text("")
        self.builder.get_object("statusProgressbar").set_fraction(0)

    def updatePackages(self, cat = None):
        for idp, package in RepositoryManager().getPackages(cat).items():
            (shortname, icon, name, desc) = package
            try:
                self.packagesList.append([shortname, gtk.gdk.pixbuf_new_from_file(icon), name, desc])
            except:
                self.packagesList.append([shortname, None, name, desc])

    def updateRepositories(self):
        self.packagesList.clear()
        
        thr = threading.Thread(target=RepositoryManager().update)
        thr.start()
        self.builder.get_object("statusText").set_text("Updating Cache")
        while thr.is_alive():
            time.sleep(0.1)
            self.builder.get_object("statusProgressbar").pulse()
            self.refreshGui()
        self.builder.get_object("statusText").set_text("")
        self.builder.get_object("statusProgressbar").set_fraction(0)
    
    def on_buttonInstall_clicked(self, widget):
        InstallerWindow(self.selected_package)
    
    def on_toolbuttonReload_clicked(self, widget):
        self.updateRepositories()
        
    def AddWineListColumn(self, title, columnId):
        """
        This function adds a column to the list view.
        First it create the gtk.TreeViewColumn and then set
        some needed properties
        """
		
        render = gtk.CellRendererText()
        	
        column = gtk.TreeViewColumn(title, render
	        , text=columnId)
        render.set_property("wrap-width", 500)
        render.set_property("wrap-mode", gtk.WRAP_WORD)
        column.set_resizable(True)
        column.set_sort_column_id(columnId)
        self.packagesView.append_column(column)
        
    def on_treeviewPackages_cursor_changed(self, widget):
        selection = self.packagesView.get_selection()
        selection.set_mode(gtk.SELECTION_SINGLE)
        tree_model, tree_iter = selection.get_selected()
        self.selected_package = tree_model.get_value(tree_iter, 0)
        if BottleManager().isInstalled(self.selected_package):
            self.buttonInstall.set_sensitive(False)
        else:
            self.buttonInstall.set_sensitive(True)
        
    def on_treeviewCats_cursor_changed(self, widget):
        selection = self.catView.get_selection()
        selection.set_mode(gtk.SELECTION_SINGLE)
        tree_model, tree_iter = selection.get_selected()
        self.selected_cat = tree_model.get_value(tree_iter, 0)
        
        self.updatePackagesList(self.selected_cat)
      
    def quit(self, widget):
        sys.exit(0)
        
    def refreshGui(self, delay=0.0001, wait=0.0001):
        """
        Use up all the events waiting to be run

        :param delay: Time to wait before using events
        :param wait: Time to wait between iterations of events

        This function will block until all pending events are emitted. This is
        useful in testing to ensure signals and other asynchronous functionality
        is required to take place.

        (c) PyGTKHelpers Authors 2005-2010
        """
        time.sleep(delay)
        while gtk.events_pending():
            gtk.main_iteration_do(block=False)
            time.sleep(wait)
        
class InstallerWindow:

    def __init__(self, shortname, packagePath = None):
        self.builder = WineCellar.helpers.get_builder('gtk_windowInstaller')
        self.window = self.builder.get_object("windowInstaller") 
        
        dic = {
            "on_buttonQuit_clicked" : self.quit,
            "on_buttonNext_clicked" : self.on_buttonNext_clicked,
            "on_windowFirstRun_destroy" : self.quit,
        }
        
        self.builder.connect_signals(dic)
        
        self.notebook = self.builder.get_object("notebook")
        
        if packagePath == None:
            self.appInfo = RepositoryManager().findApplication(shortname)
            self.notebook.set_current_page(1)
            self.builder.get_object("labelInfo").set_text("Downloading package.")
            self.window.show()
            self.builder.get_object("buttonNext").hide()
            self.builder.get_object("buttonCancel").hide()
            RepositoryManager().downloadPackage(self.appInfo[0]["url"], self.appInfo[0]["package"], self.appInfo[0]["repositoryID"], self.updateDownload)
            packagePath = self.appInfo[0]["path"]
        
        self.applicationPack = ApplicationPack(packagePath, shortname)
        self.notebook.set_current_page(0)
        self.builder.get_object("labelIntro").set_markup("<b><big>Welcome to WineCellar Installer</big></b>\nWineCellar will now setup and guide you through installing "+self.applicationPack.name)
        self.step = 0 # 0 = intro, 1 = create bottle, 2 = download, 3 = preinstall, 4 = install, 5 = finish
        self.builder.get_object("buttonNext").show()
        self.builder.get_object("buttonCancel").show()

    def on_buttonNext_clicked(self, widget = None):
        if self.step == 0:
            self.builder.get_object("buttonNext").hide()
            self.builder.get_object("buttonCancel").hide()
            self.builder.get_object("labelSpeed").set_text("")
            self.builder.get_object("labelTime").set_text("")
            self.builder.get_object("labelInfo").set_text("")
            self.builder.get_object("progressBar").set_fraction(0)
            self.notebook.set_current_page(1)
            self.step = 1

            thr = threading.Thread(target=BottleManager().create, args=(self.applicationPack.shortName,))
            thr.start()
            while thr.is_alive():
                time.sleep(0.1)
                self.builder.get_object("progressBar").pulse()
                self.refreshGui()
            
            self.on_buttonNext_clicked()
      
        elif self.step == 1:
            self.builder.get_object("buttonNext").show()
            self.builder.get_object("buttonCancel").hide()
            self.builder.get_object("labelSpeed").set_text("")
            self.builder.get_object("labelTime").set_text("")
            self.builder.get_object("labelInfo").set_text("")
            self.builder.get_object("progressBar").set_fraction(0)
            self.notebook.set_current_page(1)
            self.applicationPack.download(self.updateFile, self.updateDownload)
            self.step = 2
            self.refreshGui()    
            self.on_buttonNext_clicked()
            
        elif self.step == 2:
            #Preinstall
            self.builder.get_object("buttonNext").show()
            self.builder.get_object("buttonCancel").show()
            self.builder.get_object("labelIntro").set_markup("WineCellar installer is now going to start "+self.applicationPack.name+" installer. If a installer wizzard appears please follow the steps as you would on MS Windows.")
            self.notebook.set_current_page(0)
            self.step = 3
            
        elif self.step == 3:
            self.builder.get_object("buttonNext").hide()
            self.builder.get_object("buttonCancel").hide()
            self.builder.get_object("labelSpeed").set_text("")
            self.builder.get_object("labelTime").set_text("")
            self.builder.get_object("labelInfo").set_text("")
            self.builder.get_object("progressBar").set_fraction(0)
            self.notebook.set_current_page(1)
            self.step = 4

            thr = threading.Thread(target=self.applicationPack.installer)
            thr.start()
            while thr.is_alive():
                time.sleep(0.1)
                self.builder.get_object("progressBar").pulse()
                self.refreshGui()
            
            self.on_buttonNext_clicked()
        
        elif self.step == 4:
            #Finish
            self.builder.get_object("buttonNext").show()
            self.builder.get_object("buttonCancel").hide()
            self.builder.get_object("labelIntro").set_markup(self.applicationPack.name+" is now installed.")
            self.notebook.set_current_page(0)
            self.step = 5
            
        elif self.step == 5:
            self.window.destroy()
            
        return 0
  
    def updateFile(self, currentFile, totalFile):
        self.builder.get_object("labelInfo").set_text("Downloading "+str(currentFile)+" of "+str(totalFile))
        self.refreshGui()
    
    def updateDownload(self, blocks, blockSize, totalSize):
        currentSize = float(blocks*blockSize)
        if blocks == 0:
            self.downloadStartTime = float(time.time())
            self.speeds = []
            
        speed = float(currentSize / (time.time() - self.downloadStartTime))
        self.speeds.append(speed)
        avgSpeed = sum(self.speeds) / len(self.speeds)
        if avgSpeed < 1:
            avgSpeed = 1
        
        timeLeft = int((totalSize - currentSize) / avgSpeed)
        
        fraction = currentSize / totalSize
        if fraction > 1:
            fraction = 1

        self.builder.get_object("labelSpeed").set_text(str(helpers.humanize_bytes(avgSpeed))+"/S")
        self.builder.get_object("labelTime").set_text(str(helpers.GetInHMS(timeLeft))+" Remaining")
        self.builder.get_object("progressBar").set_fraction(fraction)
        self.refreshGui()
        
    def refreshGui(self, delay=0.0001, wait=0.0001):
        """
        Use up all the events waiting to be run

        :param delay: Time to wait before using events
        :param wait: Time to wait between iterations of events

        This function will block until all pending events are emitted. This is
        useful in testing to ensure signals and other asynchronous functionality
        is required to take place.

        (c) PyGTKHelpers Authors 2005-2010
        """
        time.sleep(delay)
        while gtk.events_pending():
            gtk.main_iteration_do(block=False)
            time.sleep(wait)

      
    def quit(self, widget):
        sys.exit(0)

def start():
    if Preferences().getKey("owner") == "n/a":
        FirstRunWindow()
    else:
        MainWindow()
    gtk.main()
